# coding: utf-8
# author: t_wolf

from utils import *


class REQUEST:
    """
    请求类: 无需实例化,用于请求中的参数定义及快速联想
    """

    def __init__(self):
        pass

    class Method:
        def __init__(self): pass

        method_list = ['get', 'post', 'put', 'delete']

    class DataType:
        def __init__(self): pass

        PARAM = 'param'
        XML = 'xml'
        JSON = 'json'

    DISPOSE_DATA = {
        DataType.XML: lambda: xml2params(web.data()),
        DataType.JSON: lambda: json2params(web.data()),
        DataType.PARAM: lambda: web.input()
    }


class RESPONSE:
    def __init__(self):
        pass

    class DataType:
        def __init__(self): pass

        PARAM = 'param'
        XML = 'xml'
        JSON = 'json'

    DISPOSE_DATA = {
        DataType.XML: lambda x: params2xml(x),
        DataType.JSON: lambda x: params2json(x),
        DataType.PARAM: lambda x: x,
    }


# 用redis实现Session.Store类
class RedisStore(Store):
    def __init__(self, redis_connection):
        self.conn = redis_connection

    def flush_timeout(self, key):
        return self.conn.expire(key, web.config.session_parameters.timeout)

    def __contains__(self, key):
        return self.conn.exists(key)

    def __getitem__(self, key):
        if key in self:
            self.flush_timeout(key)
            return self.decode(self.conn.get(key))
        else:
            raise KeyError

    def __setitem__(self, key, value):
        pickled = self.encode(value)
        self.conn.set(key, pickled, ex=web.config.session_parameters.timeout)

    def __delitem__(self, key):
        if key in self:
            self.conn.delete(key)

    def cleanup(self, timeout):
        pass


# 实现内存类, 存储Session
# MemoryStore无法进行持久化存储, 每一次重启会造成session数据丢失!
# 仅适用于简单应用场景
class MemoryStore(Store):
    def __init__(self, _storage):
        self.mem = _storage

    def __contains__(self, key):
        return key in self.mem

    def __getitem__(self, key):
        atime, v = self.mem[key]
        self[key] = v  # update atime
        return v

    def __setitem__(self, key, value):
        self.mem[key] = time.time(), value

    def __delitem__(self, key):
        try:
            del self.mem[key]
        except KeyError:
            pass

    def cleanup(self, timeout):
        now = time.time()
        for k in self.mem.keys():
            atime, v = self.mem[k]
            if now - atime > timeout:
                del self[k]


class SessionExpired(web.HTTPError):
    def __init__(self, headers, message):
        super(SessionExpired, self).__init__('200 OK', headers, data=message)


class FrSession(Session):
    def __init__(self, app, store, expired_page, initializer=None):
        initializer.update({'expired_page': expired_page})
        super(FrSession, self).__init__(app, store, initializer)

    def expired(self):
        self._killed = True
        self._save()
        # message = self._config.expired_message
        # headers = {'Content-Type': 'text/html','Redirect':'3;url="/"'}
        # raise session_expired(headers, message)
        raise web.seeother(self._initializer.get('expired_page'), True)


class SESSION:
    def __init__(self, app, initializer=None):
        self.app = app
        self.initializer = {'login': False}

        if initializer:
            self.initializer.update(initializer)
            # web.config.session_parameters.update(
            #     CONFIGS.APP.session_options
            # )

    @staticmethod
    def session_update(_options):
        web.config.session_parameters.update(_options)

    def init(self):
        _configs = web.config.apps[self.app.app_path] if web.config.apps.enabled else web.config.apps.single
        if _configs.session_options['store_type'] == 'redis':
            if _configs.redis:
                session_storage = RedisStore(_configs.redis)
            else:
                logger.critical("Session Stored Type [Redis], But redis object not created.")
        elif _configs.session_options['store_type'] == 'memory':
            session_storage = MemoryStore(Storage())
        elif _configs.session_options['store_type'] in ('mysql', 'sqlite'):
            db_sign = _configs.session_options['target_db'].strip()
            _target_db = _configs.db if db_sign in ("", "default") else getattr(_configs.db, db_sign, None)
            if _target_db:

                _target_db.query(
                    "CREATE TABLE IF NOT EXISTS %s ("
                    "session_id CHAR(128) UNIQUE NOT NULL,"
                    "atime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,"
                    "data text"
                    ")" % _configs.session_options['session_table']
                )
                session_storage = DBStore(_target_db, _configs.session_options['session_table'])
            else:
                raise ValueError(
                    "Wrong Settings for Session's [target_db], check App [%s]." %
                    self.app.app_path if web.config.apps.enabled else ROOT_PATH)
        else:
            session_storage = None
            logger.critical("Wrong Session Type [%s] for %s's Session." % (
                _configs.session_options['store_type'],
                ("App[%s]" % self.app.app_path) if web.config.apps.enabled else "Single"
            ))

        return FrSession(
            self.app,
            session_storage,
            _configs.configs.pages['expired']
            if _configs.configs.pages.get('expired', None)
            else CONFIGS.PAGES['expired'],
            initializer=self.initializer
        )

    @staticmethod
    def transfer(session):
        web.ctx.session = session

    @staticmethod
    def check_log():
        if not web.ctx.session.login:
            raise web.seeother(CONFIGS.PAGES.nolog, True)


def new_redirect(path):
    class RedirectClass:
        app_header = None

        def __init__(self):
            pass

        def GET(self, file_path):
            absolute = False
            if web.ctx.environ['PATH_INFO'].startswith("/%s" % self.app_header):
                absolute = True
            web.seeother("/static/%s/%s" % (self.app_header, file_path), absolute)

    setattr(RedirectClass, 'app_header', path)

    return RedirectClass


def pre_filter(func):
    def _pre_filter(*args):
        if len(args) != 2:
            raise web.internalerror("Wrong Arguments for pre_filter.")
        else:
            result = func(*args)
            return result if result is not None else True

    return _pre_filter


def post_filter(func):
    def _post_filter(*args):
        if len(args) != 3:
            raise web.internalerror("Wrong Arguments for post_filter.")
        else:
            return func(*args)

    return _post_filter
